package com.wuxu

import android.app.Application
import android.util.Log
import android.widget.Toast
import com.wuxu.crash_test.CrashJavaTest
import com.wuxu.lianxi_classloader.loader.PluginClassLoader
import dalvik.system.PathClassLoader

/**
 *@author wuxu
 *@date 2024/6/4 16:57
 *@description
 */
class LxHostApplication: Application() {
    private  val  TAG="LxHostApplication"
    override fun onCreate() {
        loadFix()
        super.onCreate()
    }

    private fun loadFix() {
        val apkPath = "/data/data/com.wuxu.lianxi_classloader/lianxi_plugin-debug.apk"
        try {
            val classLoader = PathClassLoader(apkPath, null, null)

//                val obj = classLoader.loadClass(crashTest.javaClass.name).newInstance()
//                this.crashTest = obj//不能直接赋值，他们是隔离的、不同对象。要通过反射把数据插入到宿主classLoader的elements中，并且创建新对象赋值给宿主crashTest变量

            //----反射合并数据到宿主来的DexClassLoader中----

            //1.获取旧ClassLoader中BaseDexClassLoader中的pathList
            val hostBaseDexClassLoaderClass = Class.forName("dalvik.system.BaseDexClassLoader")
            val hostPathList = hostBaseDexClassLoaderClass.getDeclaredField("pathList")
            hostPathList.isAccessible = true
            // 获取到旧ClassLoader中的pathList对象
            val hostPathListObj = hostPathList.get(this.classLoader)


            //2.获取DexPathList对象中的dexElements数组
            val hostDexPathListClass = Class.forName("dalvik.system.DexPathList")
            val hostDexElementsClass = hostDexPathListClass.getDeclaredField("dexElements")
            hostDexElementsClass.isAccessible = true

            val hostDexElements = hostDexElementsClass.get(hostPathListObj) as Array<*>

            //3.同理获取新ClassLoader中BaseDexClassLoader中的pathList
            val pluginBaseDexClassLoaderClass = Class.forName("dalvik.system.BaseDexClassLoader")
            val pluginPathList = pluginBaseDexClassLoaderClass.getDeclaredField("pathList")
            pluginPathList.isAccessible = true
            // 获取到旧ClassLoader中的pathList对象
            val pluginPathListObj = pluginPathList.get(classLoader)

            //4.获取新ClassLoader中的dexElements数组
            val pluginDexPathListClass = Class.forName("dalvik.system.DexPathList")
            val pluginDexElementsClass = pluginDexPathListClass.getDeclaredField("dexElements")
            pluginDexElementsClass.isAccessible = true

            val pluginDexElements = pluginDexElementsClass.get(pluginPathListObj) as Array<*>

            //5.将新数组合入到旧的DexClassLoader
            val type: Class<*> = hostDexElementsClass.type.componentType //获取隐藏类型：Element

            val combinedDexElements =
                ArrayUtil.newInstance(type, hostDexElements.size + pluginDexElements.size)

            for (i in pluginDexElements.indices) {
                Log.d(TAG, "plugin dex element : ${pluginDexElements[i]}")
                ArrayUtil.setArrayItem(combinedDexElements, i, pluginDexElements[i])
            }


            if (combinedDexElements is Array<*>) {
                combinedDexElements.forEach {
                    Log.d(TAG, "combined dex element : $it")
                }
            }

            Log.d(TAG, "--------------------------------")

            for (i in hostDexElements.indices) {
                Log.d(
                    TAG,
                    "host dex element : ${hostDexElements[i]}, index : ${pluginDexElements.size + i}"
                )
                ArrayUtil.setArrayItem(
                    combinedDexElements,
                    pluginDexElements.size + i,
                    hostDexElements[i]
                )
            }


            if (combinedDexElements is Array<*>) {
                combinedDexElements.forEach {
                    Log.d(TAG, "combined dex element : $it")
                }
            }

            Log.d(TAG, "--------------------------------")
            //6.将合成后的elements设置给宿主elements变量
            val elementClass = hostDexPathListClass.getDeclaredField("dexElements")
            elementClass.isAccessible = true
            elementClass.set(hostPathListObj, combinedDexElements)

            //创建新对象
            //合并elements不生效，排查发现CrashTest被提前加载过，在findLoadedClass这块直接返回上一次加载的实例了
            //导致合并后根本没有根据plugin element加载新的实例。
            val crashTest = this.classLoader.loadClass("com.wuxu.crash_test.CrashJavaTest")
                .newInstance() as CrashJavaTest

            crashTest.testCrash()

            Log.d(TAG, "--------------------------------")

            Toast.makeText(this, "崩溃已被修复", Toast.LENGTH_SHORT).show()
        } catch (e: ClassNotFoundException) {
            e.printStackTrace()
        }catch (e: IllegalStateException) {
            e.printStackTrace()
        }
    }


}